Skip to content

Implementation for SEP-2243 Http Standardization#1553

Open
mikekistler wants to merge 10 commits into
mainfrom
mdk/sep-2243-implementation
Open

Implementation for SEP-2243 Http Standardization#1553
mikekistler wants to merge 10 commits into
mainfrom
mdk/sep-2243-implementation

Conversation

@mikekistler
Copy link
Copy Markdown
Contributor

@mikekistler mikekistler commented May 1, 2026

Motivation and Context

Implements SEP-2243 (HTTP Header Standardization) for both client and server Streamable HTTP transports. This enables network infrastructure (load balancers, proxies, gateways) to make routing and filtering decisions based on MCP request metadata without parsing the JSON-RPC body.

Fixes #1541.

Changes

Protocol & Constants

  • Add McpHttpHeaders static class with standard header name constants (Mcp-Method, Mcp-Name, Mcp-Param-*, etc.)
  • Add DRAFT-2026-v1 as a supported protocol version for header enforcement
  • Add McpErrorCode.HeaderMismatch (-32001) for header validation failures

Client-side (ModelContextProtocol.Core)

  • StreamableHttpClientSessionTransport emits Mcp-Method, Mcp-Name, and Mcp-Param-* headers on all outgoing POST requests
  • McpHeaderEncoder handles value encoding (plain ASCII pass-through, Base64 wrapping with =?base64?...?= sentinel for non-ASCII/control characters/leading-trailing whitespace)
  • McpHeaderExtractor (internal) reads x-mcp-header annotations from tool schemas and adds corresponding Mcp-Param-{Name} headers
  • Client validates tool schemas from tools/list and rejects tools with invalid x-mcp-header annotations (empty names, invalid characters, case-insensitive duplicates, non-primitive types)

Server-side (ModelContextProtocol.AspNetCore)

  • StreamableHttpHandler validates incoming Mcp-Method and Mcp-Name headers match the JSON-RPC body (enforced only for DRAFT-2026-v1+)
  • Validates Mcp-Param-* custom headers against tool schema x-mcp-header annotations and body argument values
  • Returns 400 Bad Request with HeaderMismatch (-32001) JSON-RPC error on validation failure

Tool Authoring (ModelContextProtocol.Core)

  • [McpHeader("Name")] attribute marks tool parameters for header mirroring
  • AIFunctionMcpServerTool emits x-mcp-header extension in the tool's JSON schema for annotated parameters
  • Validates at registration time: primitive types only, case-insensitive unique names, valid ASCII characters

Protocol Version Gating

Header validation is only enforced when the client's MCP-Protocol-Version header is DRAFT-2026-v1 or later. Clients on older protocol versions are unaffected. The client always sends headers on Streamable HTTP transport regardless of version (additive, non-breaking).

New Public API Surface

ModelContextProtocol.Protocol.McpHttpHeaders (static class)

public static class McpHttpHeaders
{
    // Version gating
    public static readonly string MinVersionForStandardHeaders; // "DRAFT-2026-v1"
    public static bool SupportsStandardHeaders(string? protocolVersion);

    // Standard header name constants
    public const string SessionId = "Mcp-Session-Id";
    public const string ProtocolVersion = "MCP-Protocol-Version";
    public const string LastEventId = "Last-Event-ID";
    public const string Method = "Mcp-Method";
    public const string Name = "Mcp-Name";
    public const string ParamPrefix = "Mcp-Param-";
}

ModelContextProtocol.Protocol.McpHeaderEncoder (static class)

public static class McpHeaderEncoder
{
    // Typed overloads for compile-time safety
    public static string? EncodeValue(string? value);
    public static string EncodeValue(bool value);
    public static string EncodeValue(long value);
    public static string EncodeValue(double value);

    // General-purpose overload (supports all numeric types)
    public static string? EncodeValue(object? value);

    // Decoding (case-insensitive Base64 prefix detection)
    public static string? DecodeValue(string? headerValue);
}

ModelContextProtocol.Server.McpHeaderAttribute (attribute)

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public sealed class McpHeaderAttribute : Attribute
{
    public McpHeaderAttribute(string name);
    public string Name { get; }
}

ModelContextProtocol.McpErrorCode (enum addition)

public enum McpErrorCode
{
    HeaderMismatch = -32001,  // New
    // ... existing values unchanged
}

How Has This Been Tested?

The PR includes comprehensive tests covering:

  • Unit tests: McpHeaderEncoderTests — encode/decode round-trips, Base64 edge cases, type conversion, empty/null handling
  • Unit tests: McpHeaderAttribute validation and schema generation
  • Conformance tests: Sep2243HeaderTests — server-side header validation (method/name mismatch, missing headers, Base64 encoding, version gating, custom parameter headers)
  • Integration tests: End-to-end client→server header flow via in-memory transport

Conformance tests are aligned with conformance repo PR #259.

All existing tests continue to pass (~8,000+ across net8.0, net9.0, net10.0, net472).

Breaking Changes

No breaking changes.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

mikekistler and others added 6 commits May 1, 2026 09:30
Address multiple specification compliance issues identified during review:

Source fixes:
- Fix tab (0x09) encoding: tabs now trigger Base64 encoding per spec
- Add client-side tool schema validation: tools with invalid x-mcp-header
  annotations (non-object schemas, missing properties) are filtered out
- Remove server empty-header early-return that skipped validation
- Fix numeric precision loss: use GetRawText()/ToJsonString() instead of
  GetDouble()/GetValue<double>() for header value extraction
- Implement proper version gating: only DRAFT-2026-v1 requires header
  validation on server side (client always sends headers unconditionally)
- Add server-side invalid character validation for header values

Test additions:
- 22 new integration tests in Sep2243HeaderTests.cs covering encoding,
  validation, tool filtering, and end-to-end header scenarios
- 4 new server conformance tests for draft version header validation
- 2 new client conformance tests for tool filtering and header sending
- 2 new unit tests for tab encoding behavior
- 1 new theory for SupportsStandardHeaders version gating
- Fix ConformanceClient to handle toolCalls array format and add
  prompts/list + prompts/get calls for http-standard-headers scenario

Other:
- Add *.lscache to .gitignore
The http-standard-headers, http-custom-headers, and http-invalid-tool-headers
client scenarios and http-header-validation, http-custom-header-server-validation
server scenarios require conformance package >= 0.1.16 which is not yet
published to npm.

Add NodeHelpers.GetConformanceVersion() that runs 'conformance --version'
and NodeHelpers.IsConformanceVersionAtLeast() for runtime version checks.
Tests auto-skip when the harness is too old and auto-run once the package
is updated. Works with both npm-published and local file: references.
Tarek Mahmoud Sayed and others added 4 commits May 10, 2026 12:00
…rash

The Node.js conformance runner can crash during cleanup on Windows with a
libuv assertion (UV_HANDLE_CLOSING) that produces a non-zero exit code even
when all conformance checks passed. This change adds a fallback that parses
the 'Test Results:' summary in stdout to determine success when the process
exit code is non-zero.

Also updates package-lock.json with latest dependency versions.
McpHeaderEncoder is a shared utility used by both client-side (McpHeaderExtractor)
and server-side (StreamableHttpHandler) code. Placing it in the Client namespace
forced server code to reference ModelContextProtocol.Client, creating a misleading
dependency. Move it to ModelContextProtocol.Protocol alongside McpHttpHeaders where
it logically belongs as a transport-level concern.
- Add typed EncodeValue overloads (string?, bool, long, double) to McpHeaderEncoder
  for stronger compile-time safety and non-nullable return types
- Change MinVersionForStandardHeaders from const to static readonly to prevent
  const-inlining across assemblies when the version string changes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SEP-2243: HTTP Standardization

3 participants